package com.flux.ui.widget

import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.graphics.drawable.ShapeDrawable
import android.support.v4.view.ViewPager
import android.util.AttributeSet
import android.view.View

import com.flux.R

import java.util.ArrayList

/**
 * Created by hsy on 2016/4/11.
 */
class DPCircleIndicator : View {
    private var viewPager: ViewPager? = null
    private var tabItems: MutableList<DrawableHolder>? = null
    private var movingItem: DrawableHolder? = null

    //config list
    private var mCurItemPosition: Int = 0
    private var mCurItemPositionOffset: Float = 0.toFloat()
    private var mIndicatorRadius: Float = 0.toFloat()
    private var mIndicatorMargin: Float = 0.toFloat()
    private var mIndicatorBackground: Drawable? = null
    private var mIndicatorSelectedBackground: Drawable? = null
    private var mIndicatorLayoutGravity: Gravity? = null
    private var mIndicatorMode: Mode? = null

    //default value
    private val DEFAULT_INDICATOR_RADIUS = 10
    private val DEFAULT_INDICATOR_MARGIN = 40
    private val DEFAULT_INDICATOR_BACKGROUND = Color.BLUE
    private val DEFAULT_INDICATOR_SELECTED_BACKGROUND = Color.RED
    private val DEFAULT_INDICATOR_LAYOUT_GRAVITY = Gravity.CENTER.ordinal
    private val DEFAULT_INDICATOR_MODE = Mode.SOLO.ordinal

    enum class Gravity {
        LEFT,
        CENTER,
        RIGHT
    }

    enum class Mode {
        INSIDE,
        OUTSIDE,
        SOLO
    }

    constructor(context: Context) : super(context) {
        init(context, null)
    }

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
        init(context, attrs)
    }

    constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
        init(context, attrs)
    }

    private fun init(context: Context, attrs: AttributeSet?) {
        tabItems = ArrayList()
        handleTypedArray(context, attrs)
    }

    private fun handleTypedArray(context: Context, attrs: AttributeSet?) {
        if (attrs == null)
            return
        val typedArray = context.obtainStyledAttributes(attrs, R.styleable.DPCircleIndicator)
        mIndicatorRadius = typedArray.getDimensionPixelSize(R.styleable.DPCircleIndicator_ci_radius, DEFAULT_INDICATOR_RADIUS).toFloat()
        mIndicatorMargin = typedArray.getDimensionPixelSize(R.styleable.DPCircleIndicator_ci_margin, DEFAULT_INDICATOR_MARGIN).toFloat()
        mIndicatorBackground = typedArray.getDrawable(R.styleable.DPCircleIndicator_ci_background)
        mIndicatorSelectedBackground = typedArray.getDrawable(R.styleable.DPCircleIndicator_ci_selected_background)
        val gravity = typedArray.getInt(R.styleable.DPCircleIndicator_ci_gravity, DEFAULT_INDICATOR_LAYOUT_GRAVITY)
        mIndicatorLayoutGravity = Gravity.values()[gravity]
        val mode = typedArray.getInt(R.styleable.DPCircleIndicator_ci_mode, DEFAULT_INDICATOR_MODE)
        mIndicatorMode = Mode.values()[mode]
        typedArray.recycle()
    }

    fun setViewPager(viewPager: ViewPager) {
        this.viewPager = viewPager
        this.tabItems!!.clear()
        createTabItems()
        createMovingItem()
        setUpListener()
    }

    private fun setUpListener() {
        viewPager!!.addOnPageChangeListener(object : ViewPager.SimpleOnPageChangeListener() {

            override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
                super.onPageScrolled(position, positionOffset, positionOffsetPixels)
                if (mIndicatorMode != Mode.SOLO) {
                    trigger(position, positionOffset)
                }
            }

            override fun onPageSelected(position: Int) {
                super.onPageSelected(position)
                if (mIndicatorMode == Mode.SOLO) {
                    trigger(position, 0f)
                }
            }
        })
    }

    /**
     * trigger to redraw the indicator when the ViewPager's selected item changed!
     *
     * @param position
     * @param positionOffset
     */
    private fun trigger(position: Int, positionOffset: Float) {
        this@DPCircleIndicator.mCurItemPosition = position
        this@DPCircleIndicator.mCurItemPositionOffset = positionOffset
        requestLayout()
        invalidate()
    }

    private fun createTabItems() {
        for (i in 0 until viewPager!!.adapter!!.count) {
            tabItems!!.add(DrawableHolder(mIndicatorBackground!!))
        }
    }

    private fun createMovingItem() {
        movingItem = DrawableHolder(mIndicatorSelectedBackground!!)
    }

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        super.onLayout(changed, left, top, right, bottom)
        val width = width
        val height = height
        layoutTabItems(width, height)
        layoutMovingItem(mCurItemPosition, mCurItemPositionOffset)
    }

    private fun layoutTabItems(containerWidth: Int, containerHeight: Int) {
        if (tabItems == null) {
            throw IllegalStateException("forget to create tabItems?")
        }
        val yCoordinate = containerHeight * 0.5f
        val startPosition = startDrawPosition(containerWidth)
        for (i in tabItems!!.indices) {
            val item = tabItems!![i]
            item.resizeShape(2 * mIndicatorRadius, 2 * mIndicatorRadius)
            item.y = yCoordinate - mIndicatorRadius
            val x = startPosition + (mIndicatorMargin + mIndicatorRadius * 2) * i
            item.x = x
        }

    }

    private fun startDrawPosition(containerWidth: Int): Float {
        if (mIndicatorLayoutGravity == Gravity.LEFT)
            return 0f
        val tabItemsLength = tabItems!!.size * (2 * mIndicatorRadius + mIndicatorMargin) - mIndicatorMargin
        if (containerWidth < tabItemsLength) {
            return 0f
        }
        return if (mIndicatorLayoutGravity == Gravity.CENTER) {
            (containerWidth - tabItemsLength) / 2
        } else containerWidth - tabItemsLength
    }

    private fun layoutMovingItem(position: Int, positionOffset: Float) {
        if (movingItem == null) {
            //            throw new IllegalStateException("forget to create movingItem?");
            return
        }
        val item = tabItems!![position]
        movingItem!!.resizeShape(item.width, item.height)
        val x = item.x + (mIndicatorMargin + mIndicatorRadius * 2) * positionOffset
        movingItem!!.x = x
        movingItem!!.y = item.y

    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        val sc = canvas.saveLayer(0f, 0f, width.toFloat(), height.toFloat(), null, Canvas.ALL_SAVE_FLAG)
        for (item in tabItems!!) {
            canvas.save()
            canvas.translate(item.x, item.y)
            item.drawable!!.draw(canvas)
            canvas.restore()
        }

        if (movingItem != null) {
            canvas.save()
            canvas.translate(movingItem!!.x, movingItem!!.y)
            movingItem!!.drawable!!.draw(canvas)
            canvas.restore()
        }
        canvas.restoreToCount(sc)
    }

    fun setIndicatorRadius(mIndicatorRadius: Float) {
        this.mIndicatorRadius = mIndicatorRadius
    }

    fun setIndicatorMargin(mIndicatorMargin: Float) {
        this.mIndicatorMargin = mIndicatorMargin
    }

    fun setIndicatorBackground(mIndicatorBackground: Drawable) {
        this.mIndicatorBackground = mIndicatorBackground
    }

    fun setIndicatorSelectedBackground(mIndicatorSelectedBackground: Drawable) {
        this.mIndicatorSelectedBackground = mIndicatorSelectedBackground
    }

    fun setIndicatorLayoutGravity(mIndicatorLayoutGravity: Gravity) {
        this.mIndicatorLayoutGravity = mIndicatorLayoutGravity
    }

    fun setIndicatorMode(mIndicatorMode: Mode) {
        this.mIndicatorMode = mIndicatorMode
    }

    inner class DrawableHolder(s: Drawable) {
        var x = 0f
        var y = 0f
        var drawable: Drawable? = null
            private set

        var width: Float
            get() {
                val rect = drawable!!.bounds
                return (rect.right - rect.left).toFloat()
            }
            set(width) {
                val rect = drawable!!.bounds
                drawable!!.setBounds(rect.left, rect.top, (rect.left + width).toInt(), rect.bottom)
            }

        var height: Float
            get() {
                val rect = drawable!!.bounds
                return (rect.bottom - rect.top).toFloat()
            }
            set(height) {
                val rect = drawable!!.bounds
                drawable!!.setBounds(rect.left, rect.top, rect.right, (rect.top + height).toInt())
            }

        fun setShape(value: ShapeDrawable) {
            drawable = value
        }

        fun resizeShape(width: Float, height: Float) {
            this.width = width
            this.height = height
        }

        init {
            drawable = s
        }
    }
}
